/*================================================================
*   Copyright (C) 2017 LiuYao. All rights reserved.
*   
*   Filename : matrix.h
*   Author : LiuYao
*   Date : 2017-11-27
*   Description : 
*
================================================================*/
#ifndef _MATRIX_H
#define _MATRIX_H

#define TAB_SEP "\t"
#define LINE_SEP "\n"

#include <vector>
#include <iostream> 
#include <string>
#include "../exception/size_exception.h"
#include "../exception/divide_zero_exception.h"
using std::vector;
using std::ostream;
using std::cout;
using std::endl;
using std::string;

struct Shape{
    int rows;
    int cols;
    Shape(int rows, int cols) : rows(rows), cols(cols){}
    Shape(const Shape & shape) : rows(shape.rows), cols(shape.cols){}
    Shape & operator=(const Shape & shape);
    friend ostream & operator<<(ostream & os, const Shape & shape);
};

class Matrix{
    private:
        double** data_;
        int rows_;
        int cols_;
        void init(int, int, double**);
    
    public:
        Matrix() : Matrix(0,0){};
        Matrix(int, int, double = 0);
        Matrix(const Matrix &);
        Matrix(Matrix&&) noexcept;
        Matrix(const vector<vector<double>>& value);
        ~Matrix();

        int getRows() const { return rows_; }
        int getCols() const { return cols_; }
        Shape getShape() const { return Shape(rows_, cols_); }
        Matrix transpose();

        double* operator[](int) const;
        Matrix& operator=(const Matrix&);

        friend Matrix ones_like(const Matrix&);
        friend Matrix zeros_like(const Matrix&);
        friend ostream& operator<<(ostream &, const Matrix &);
        friend Matrix & operator+=(Matrix &, const Matrix &);
        friend Matrix operator+(const Matrix &, const Matrix &);
        friend Matrix operator+(const Matrix &, const double &);
        friend Matrix operator+(const double &, const Matrix &);
        friend Matrix operator-(const Matrix &, const Matrix &);
        friend Matrix operator-(const Matrix &, const double &);
        friend Matrix operator-(const double &, const Matrix &);
        friend Matrix operator*(const Matrix &, const Matrix &);
        friend Matrix operator*(const Matrix &, const double &);
        friend Matrix operator*(const double &, const Matrix &);
        friend Matrix operator/(const Matrix &, const Matrix &) throw(DivideZeroException);
        friend Matrix operator/(const Matrix &, const double &) throw(DivideZeroException);
        friend Matrix operator/(const double &, const Matrix &) throw(DivideZeroException);
        friend Matrix pow(const Matrix &, const Matrix &);
        friend Matrix matmul(const Matrix &, const Matrix &);
        friend Matrix exp(const Matrix &);
        friend Matrix log(const Matrix &);
        friend double sum(const Matrix &);
        friend bool checkSizeImcompatible(const Matrix&, const Matrix&) throw(SizeException);
        friend bool MatOpSizeImcompatible(const Matrix&, const Matrix&) throw(SizeException);
};

Matrix readDigitalCSV(string);

Matrix random_matrix(int rows, int cols);

#endif
